"""
主题: 给关键部分加锁
问题: 你需要对多线程程序中的临界区加锁以避免竞争条件。
提示 : 
"""

import threading

class SharedCounter:
    '''
    A counter object that can be shared by multiple threads.
    '''
    def __init__(self, initial_value = 0):
        self._value = initial_value
        self._value_lock = threading.Lock()

    def incr(self,delta=1):
        '''
        Increment the counter with locking
        '''
        with self._value_lock:
             self._value += delta

    def decr(self,delta=1):
        '''
        Decrement the counter with locking
        '''
        with self._value_lock:
             self._value -= delta

class SharedCounterExt:
    '''所有实例共享的类级锁'''
    '''
    A counter object that can be shared by multiple threads.
    '''
    _lock = threading.RLock()
    def __init__(self, initial_value = 0):
        self._value = initial_value

    def incr(self,delta=1):
        '''
        Increment the counter with locking
        '''
        with SharedCounter._lock:
            self._value += delta

    def decr(self,delta=1):
        '''
        Decrement the counter with locking
        '''
        with SharedCounter._lock:
             self.incr(-delta)             

from threading import Semaphore
import urllib.request

# At most, five threads allowed to run at once
_fetch_url_sema = Semaphore(5)
def fetch_url(url):
    '''限制一段代码的并发访问量'''
    with _fetch_url_sema:
        return urllib.request.urlopen(url)
    
def recipe1():
    def test1(c):
        for _ in range(100000):
            c.incr()

    def test2(c):
        for _ in range(100000):
            c.decr()

    c = SharedCounter()
    t1 = threading.Thread(target=test1, args=(c,))
    t2 = threading.Thread(target=test2, args=(c,))
    t3 = threading.Thread(target=test1, args=(c,))
    t4 = threading.Thread(target=test2, args=(c,))
    t1.start()
    t2.start()
    t3.start()
    t4.start()
    print('Running test')
    t1.join()
    t2.join()
    t3.join()
    t4.join()
    
    assert c._value == 0
    print('Looks good!')
    

def main():
    print('recipe1'.center(20, '*'))
    recipe1()


if __name__ == '__main__':
    main()                                